/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package models;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.JoinTable;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import play.db.jpa.Model;

/**
 * 
 * @author Ousama
 */
@Entity
public class Organization extends Model {

    public int noOfMembers;
    public String name;
    public Date date;
    public int views;
    public boolean deactivated = false;
    public String type;// new attribute .. shof mafrod yzeed fen tany!
    @Lob
    public String description;
    public String website;
    public String telephone;
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "Organization1")
    public List<IdeaDeveloper> members = new ArrayList<IdeaDeveloper>();
    @ManyToMany
    @JoinTable(name = "Organization10")
    public List<OrganizationLead> managers = new ArrayList<OrganizationLead>();
    @ManyToOne
    @JoinTable(name = "Organization11")
    public OrganizationLead creator;
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "Organization2")
    public List<IdeaDeveloper> blocked = new ArrayList<IdeaDeveloper>();
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "Organization3")
    public List<IdeaDeveloper> followed = new ArrayList<IdeaDeveloper>();
    @OneToMany(mappedBy = "liesIn", cascade = CascadeType.ALL)
    public List<Entities> liesIn = new ArrayList<Entities>();
    @OneToMany(mappedBy = "topicOrg", cascade = CascadeType.ALL)
    public List<Topic> topics = new ArrayList<Topic>();
    @OneToMany(mappedBy = "ideaOrg", cascade = CascadeType.ALL)
    public List<Idea> ideas = new ArrayList<Idea>();
    @OneToMany(mappedBy = "org", cascade = CascadeType.ALL)
    public List<Request> requests = new ArrayList<Request>();
    @OneToMany(mappedBy = "hasNotifications", cascade = CascadeType.ALL)
    public List<Notification> notifications = new ArrayList<Notification>();
    @OneToMany(mappedBy = "organization", cascade = CascadeType.ALL)
    public List<Rate> rates = new ArrayList<Rate>();
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "Organizatio10")
    public List<IdeaDeveloper> unfollowed = new ArrayList<IdeaDeveloper>();

    /**
     * @author Ousama
     *
     *         This method for adding an entity in the organization and checks
     *         first the scenarios which are I) The name is dublicated II)The
     *         adder is not allowed III)Everything is ok and it is added
     * @param entityName
     *            the name for the new entitiy
     * @param adderDeveloper
     *            the developer which asks to add that entitiy
     * @return Status String to tell the user the result of his request
     *
     */
    public String addEntitiy(String entityName, IdeaDeveloper adderDeveloper) {
        try {
            if (Entities.count("name = ? AND liesIn = ?", entityName, this) != 0) {
                return "The Name Is Not Avaliable";
            }
            OrganizationLead lead = OrganizationLead.find("ideaDeveloper = ?",
                    adderDeveloper).first();
            if (!managers.contains(lead)) {
                return "You Are Not Allowed";
            }
            Entities addedEntity = Entities.create(this);
            addedEntity.name = entityName;
            Organizer organizer = Organizer.find("ideaDeveloper = ?",
                    adderDeveloper).first();
            if (organizer == null) {
                organizer = Organizer.create(lead.ideaDeveloper);
            }
            organizer.manages.add(addedEntity);
            organizer.save();
            // adderDeveloper.joinedEnt.add(addedEntity);
            adderDeveloper.followedEnt.add(addedEntity);
            adderDeveloper.incrementCounter(10);
            adderDeveloper.save();
            addedEntity.managers.add(organizer);
            // addedEntity.members.add(lead.ideaDeveloper);
            addedEntity.followed.add(adderDeveloper);
            addedEntity.save();
            Log log = new Log("^ added ^ in ^");
            log.ids = adderDeveloper.id + "," + addedEntity.id + "," + this.id;
            log.types = "developer,entity,organization";
            log.save();
            for (int i = 0; i < this.followed.size(); i++) {
                Notification.notify(" has added the entity <a href='/application/entityview?entityId?entityId="
                        + addedEntity.id + "'>" + addedEntity.name + "</a>", this.followed.get(i), adderDeveloper, new Date());
            }
            return "Done Adding";

        } catch (Exception exp) {
            return "You Are Not Allowed";
        }
    }

    public Entities addEntitiyReturn(String entityName,
            IdeaDeveloper adderDeveloper) { // returns the added entity in the
        // organization
        try {
            if (Entities.count("name = ? AND liesIn = ?", entityName, this) != 0) {
                return null;
            }
            OrganizationLead lead = OrganizationLead.find("ideaDeveloper = ?",
                    adderDeveloper).first();
            if (!managers.contains(lead)) {
                return null;
            }
            Entities addedEntity = Entities.create(this);
            addedEntity.name = entityName;
            Organizer organizer = Organizer.create(lead.ideaDeveloper);
            organizer.manages.add(addedEntity);
            organizer.save();
            adderDeveloper.joinedEnt.add(addedEntity);
            adderDeveloper.save();
            addedEntity.managers.add(organizer);
            addedEntity.members.add(lead.ideaDeveloper);
            addedEntity.save();
            Log log = new Log("^ added Entity ^ in Organization ^");
            log.ids = adderDeveloper.id + "," + addedEntity.id + "," + this.id;
            log.types = "developer,entity,organization";
            log.save();
            for (int i = 0; i < this.followed.size(); i++) {
                Notification.notify(" has added the entity <a href='/application/entityview?entityId?entityId="
                        + addedEntity.id + "'>" + addedEntity.name + "</a>", this.followed.get(i), adderDeveloper, new Date());
            }
            return addedEntity;

        } catch (Exception exp) {
            return null;
        }
    }

    /**
     * @author Ousama
     * @param lead
     *            the leader
     * @param name
     *            the name of the org
     * @return the Organization really added
     */
    public static Organization create(OrganizationLead lead, String name) {
        Organization org = new Organization();
        org.creator = lead;
        org.managers.add(lead);
        org.date = new Date();
        org.views = 0;
        // org.members.add(lead.ideaDeveloper);
        org.type = "public";
        org.noOfMembers = 0;
        org.save();
        lead.manages.add(org);
        lead.createdOrgs.add(org);
        // lead.ideaDeveloper.joinedOrg.add(org);
        lead.save();
        org.name = name;
        org.save();
        Log log = new Log("^ added Organization ^");
        log.ids = lead.ideaDeveloper.id + "," + org.id;
        log.types = "developer,organization";
        log.save();
        return org;
    }

    /**
     * @author nataly and dalia
     * @param leadDeveloper
     * @param name
     * @param description
     * @param privacy
     * @return
     */
    public static Organization create(IdeaDeveloper leadDeveloper, String name,
            String description, String privacy) {
        OrganizationLead lead = new OrganizationLead();
        if (OrganizationLead.count("ideaDeveloper = ?", leadDeveloper) == 0) {
            lead = OrganizationLead.create(leadDeveloper);
            lead.save();
        } else {
            lead = OrganizationLead.find("ideaDeveloper like ?", leadDeveloper).first();
        }
        Organization org = new Organization();
        org.creator = lead;
        org.managers.add(lead);
        org.date = new Date();
        org.description = description;
        org.views = 0;
        org.type = privacy;
        // org.members.add(lead.ideaDeveloper);
        org.noOfMembers = 0;
        org.save();
        lead.manages.add(org);
        lead.createdOrgs.add(org);
        lead.ideaDeveloper.followedOrg.add(org);
        // lead.ideaDeveloper.joinedOrg.add(org);
        lead.save();
        org.name = name;
        org.followed.add(leadDeveloper);
        org.save();
        Log log = new Log("^ added Organization ^");
        log.ids = lead.ideaDeveloper.id + "," + org.id;
        log.types = "developer,organization";
        log.save();
        return org;
    }

    /**
     * @author Ousama
     * @param lead
     *            the leader
     * @param name
     *            the name of the org
     * @return the Organization really added
     */
    public static Organization create(OrganizationLead lead, String name,
            String description) {
        Organization org = new Organization();
        org.creator = lead;
        org.managers.add(lead);
        org.date = new Date();
        org.description = description;
        org.views = 0;
        // org.members.add(lead.ideaDeveloper);
        org.type = "public";
        org.noOfMembers = 0;
        org.save();
        lead.manages.add(org);
        lead.createdOrgs.add(org);
        // lead.ideaDeveloper.joinedOrg.add(org);
        lead.save();
        org.name = name;
        org.save();
        Log log = new Log("^ added ^");
        log.ids = lead.ideaDeveloper.id + "," + org.id;
        log.types = "developer,organization";
        log.save();
        return org;
    }

    public void delete2() {
        // delete the requests
        while (!requests.isEmpty()) {
            requests.get(0).delete2();
        }

        // delete the ideas
        while (!ideas.isEmpty()) {
            ideas.get(0).delete2();
        }
        // delete the topics
        while (!topics.isEmpty()) {
            topics.get(0).delete2();
        }
        // delete the entities
        while (!liesIn.isEmpty()) {
            liesIn.get(0).delete2();
        }
        // delete it from the followers
        for (int i = 0; i < followed.size(); i++) {
            followed.get(i).followedOrg.remove(this);
            followed.get(i).save();
        }

        for (int i = 0; i < unfollowed.size(); i++) {
            unfollowed.get(i).unfollowedOrg.remove(this);
            unfollowed.get(i).save();
        }

        // delete it from the blocked
        for (int i = 0; i < blocked.size(); i++) {
            blocked.get(i).blockedOrg.remove(this);
            blocked.get(i).save();
        }
        // deletes it from the creator;s
        creator.createdOrgs.remove(this);
        creator.save();
        // delete it from the managers
        for (int i = 0; i < managers.size(); i++) {
            managers.get(i).manages.remove(this);
            managers.get(i).save();
        }

        // delete it from the members
        for (int i = 0; i < members.size(); i++) {
            members.get(i).joinedOrg.remove(this);
            members.get(i).save();
        }
        while (!rates.isEmpty()) {
            rates.get(0).delete2();
        }


        Log log = new Log("^ was deleted.");
        log.ids = this.id + "";
        log.types = "organization";
        log.save();
        this.delete();
    }

    /**
     * takes idea developer and organization to be followed and checks if the
     * user is already a follower
     *
     * @author nataly.wadie
     * @param follower
     *            idea developer that wants to follow organization
     * @param organization
     *            organization that will be followed by idea developer
     * @return return true if the user is already following else return false
     */
    public static boolean followCheck(IdeaDeveloper follower,
            Organization organization) {
        return organization.followed.contains(follower);
    }

    /**
     * takes idea developer and organization and checks if he's not a follower
     * then add the follower in the list of followers in the organization and
     * add the organization in the list of followed organizations at the
     * follower
     *
     * @author nataly.wadie
     * @param follower
     *            idea developer that wants to follow organization
     * @param organization
     *            organization that is going to be followed by idea developer
     */
    public static void followOrganization(IdeaDeveloper follower,
            Organization organization) {

        if (!followCheck(follower, organization)) {
            organization.followed.add(follower);
            organization.save();
            follower.followedOrg.add(organization);
            follower.save();
            if (organization.unfollowed.contains(follower)) {
                organization.unfollowed.remove(follower);
                organization.save();
                follower.unfollowedOrg.remove(organization);
                follower.save();
            }
            Log log = new Log("^ followed ^");
            log.ids = follower.id + "," + organization.id;
            log.types = "developer,organization";
            log.save();
            for (int i = 0; i < organization.managers.size(); i++) {
                Notification.notify(
                        " has followed the organization <a href='/application/organizationview?name="
                        + organization.name + "'>" + organization.name
                        + "</a>",
                        organization.managers.get(i).ideaDeveloper, follower,
                        new Date());
            }
        }

    }

    /**
     * takes an idea developer and an organization and checks if the user is
     * already a follower then he'll be removed from the followers lists
     *
     * @author nataly.wadie
     * @param unfollower
     *            the user that doesn't want to receive anymore notifications
     *            about the organization
     * @param organization
     *            the organization that will be unfollowed
     */
    public static void unfollowOrganization(IdeaDeveloper unfollower,
            Organization organization) {
        if (followCheck(unfollower, organization)) {
            organization.followed.remove(unfollower);
            organization.save();
            unfollower.followedOrg.remove(organization);
            unfollower.save();
            if (!organization.unfollowed.contains(unfollower)) {
                organization.unfollowed.add(unfollower);
                organization.save();
                unfollower.unfollowedOrg.add(organization);
                unfollower.save();
            }
            Log log = new Log("^ unfollowed ^");
            log.ids = unfollower.id + "," + organization.id;
            log.types = "developer,organization";
            log.save();
            for (int i = 0; i < organization.managers.size(); i++) {
                Notification.notify(
                        " has unfollowed the organization <a href='/application/organizationview?name="
                        + organization.name + "'>" + organization.name
                        + "</a>",
                        organization.managers.get(i).ideaDeveloper, unfollower,
                        new Date());
            }
        }
    }

    /**
     * checks if a given ideadevoloper is a memeber of a specifc organzaion
     *
     * @author XtarX
     * @param ideaDevoloperId
     * @return true if the ideadevloper is a member of the organization , else
     *         returns false
     */
    public static boolean isMember(Long ideaDevoloperId) {

        Organization organization = new Organization();
        IdeaDeveloper ideaDev = IdeaDeveloper.findById(ideaDevoloperId);

        for (int i = 0; i < organization.members.size(); i++) {

            if (organization.members.contains(ideaDev)) {

                return true;
            }
        }
        return false;

    }

    /**
     * increment the view of organization every time the page is loaded
     *
     * @author XtarX
     * @param orgId
     * @return the number of views incremented
     */
    public static int incrementViews(Long orgId) {
        Organization organiztin = Organization.findById(orgId);
        organiztin.views += 1;
        organiztin.save();
        return organiztin.views;
    }

    /**
     * returns the name to the organzion instead of id to be used in the admin
     * view
     */
    public String toString() {
        return this.name;
    }
}
